'use strict';

const threads = require ( 'worker_threads' );
const { Worker, isMainThread } = threads;

const LOCKED = 0;
const UNLOCKED = 1;

class BinarySemaphore {
    constructor ( shared, offset = 0, init = false ) {
        this.lock = new Int32Array ( shared, offset, 1 );
        if ( init ) Atomics.store ( this.lock, 0, UNLOCKED );
    }

    enter () {
        let prev = Atomics.exchange ( this.lock, 0, LOCKED );
        while ( prev !== UNLOCKED ) {
            Atomics.wait ( this.lock, 0, LOCKED );
            prev = Atomics.exchange ( this.lock, 0, LOCKED );
        }
    }

    leave () {
        if ( Atomics.load ( this.lock, 0 ) === UNLOCKED ) {
            throw new Error ( 'Cannot leave unlocked BinarySemaphore' );
        }
        Atomics.store ( this.lock, 0, UNLOCKED );
        Atomics.notify ( this.lock, 0, 1 );
    }
}

// Usage

if ( isMainThread ) {
    const buffer = new SharedArrayBuffer ( 14 );
    const semaphore = new BinarySemaphore ( buffer, 0, true );
    console.dir ( { semaphore } );
    new Worker ( __filename, { workerData: buffer } );
    new Worker ( __filename, { workerData: buffer } );
}
else {
    const { threadId, workerData } = threads;
    const semaphore = new BinarySemaphore ( workerData );
    const array = new Int8Array ( workerData, 4 );
    const value = threadId === 1 ? 1 : -1;

    setInterval ( () => {
        semaphore.enter ();
        for ( let i = 0 ; i < 10 ; i++ ) {
            array[i] += value;
        }
        console.dir ( [ threadId, semaphore.lock[0], array ] );
        semaphore.leave ();
    }, 100 ); // change to 10 to see race condition
}
